package com.henghe.frame.ddl.util;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.henghe.frame.common.entity.BaseEntity;
import com.henghe.frame.common.util.CamelUtil;
import com.henghe.frame.ddl.constant.DdlConstant;
import com.henghe.frame.ddl.pojo.TableColumnEntity;
import com.henghe.frame.ddl.pojo.TableEntity;
import com.henghe.frame.ddl.pojo.mysql.MysqlTableEntity;
import com.henghe.frame.ddl.annotation.Comment;
import com.henghe.frame.ddl.pojo.postgres.PostgresTableEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

/**
 * Description:
 * Author: LiuWenQing
 * Datetime: 2024/6/5 10:01
 */
@Slf4j
public class DbUtil {

    /**
     * @param packagePath
     * @param type
     * @return
     */
    public static List<TableEntity> searchPackageEntities(String packagePath, String type, String engineType, List<Class> generatorClasses, String schema, String dbName) {
        Reflections reflections = new Reflections(packagePath);
        Set<Class<?>> typesAnnotated = reflections.getTypesAnnotatedWith(TableName.class);

        List<TableEntity> tableEntities = new ArrayList<>();
        for (Class<?> aClass : typesAnnotated) {
            switch (type) {
                case DdlConstant.MYSQL:
                    Boolean flag = handleMysqlTableEntities(tableEntities, engineType, aClass, generatorClasses);
                    if (!flag)
                        continue;
                    break;
                case DdlConstant.PG:
                    Boolean pgFlag = handlePgTableEntities(tableEntities, aClass, generatorClasses, schema, dbName);
                    if (!pgFlag)
                        continue;
                    break;
                default: {
                }
            }
        }
        return tableEntities;
    }

    /**
     * 将实体类反射成mysql类型
     *
     * @param tableEntities
     * @param aClass
     * @param generatorClasses
     * @return
     */
    private static Boolean handleMysqlTableEntities(List<TableEntity> tableEntities, String engineType, Class<?> aClass, List<Class> generatorClasses) {
        MysqlTableEntity mysqlTableEntity = new MysqlTableEntity();
        TableName table = aClass.getAnnotation(TableName.class);
        if (table == null || StringUtils.isEmpty(table.value()))
            return false;

        List<Field> fields = Arrays.asList(aClass.getDeclaredFields());
        Boolean tableAnnotationCheck = checkTableAnnotation(fields, generatorClasses);
        if (!tableAnnotationCheck)
            return false;
        mysqlTableEntity.setEngineType(engineType.equals("innodb") ? 1 : 2);
        mysqlTableEntity.setTableName(table.value());
        List<TableColumnEntity> columns = new ArrayList<>();
        for (Field field : fields) {
            if (!generatorClasses.contains(field.getType()))
                continue;
            TableField tableFieldAno = field.getAnnotation(TableField.class);
            if (tableFieldAno != null && tableFieldAno.exist() == false)
                continue;
            if (field.getName().equals("serialVersionUID"))
                continue;
            TableColumnEntity column = new TableColumnEntity();
            TableId tableIdAno = field.getAnnotation(TableId.class);
            if (tableIdAno != null) {
                // id
                column.setName(StringUtils.isNotEmpty(tableIdAno.value()) ? tableIdAno.value() : "id");
                column.setComment(DdlConstant.idComment);
                column.setIsPrimaryKey(true);
                column.setIsNotNull(Boolean.TRUE);
                if (tableIdAno.type() == null) {
                    column.setType("bigint");
                    column.setGenerateType("AUTO_INCREMENT");
                    column.setAutoIncrement(1);
                    column.setLength("20");
                } else {
                    switch (tableIdAno.type()) {
                        case NONE:
                        case AUTO:
                            column.setType("bigint");
                            column.setGenerateType("AUTO_INCREMENT");
                            column.setAutoIncrement(1);
                            column.setLength("20");
                            break;
                        case INPUT:
                            if (field.getType().equals(String.class)) {
                                column.setType("varchar");
                                column.setLength("32");
                            } else if (field.getType().equals(int.class) ||
                                    field.getType().equals(Integer.class)) {
                                column.setType("bigint");
                                column.setLength("20");
                            } else {
                            }
                            break;
                        case ASSIGN_ID:
                            column.setType("bigint");
                            column.setLength("20");
                            break;
                        case ASSIGN_UUID:
                            column.setType("varchar");
                            column.setLength("32");
                            break;
                        default: {
                        }
                    }
                }
                columns.add(column);
                continue;
            }
            column.setName(CamelUtil.camelToUnderline(field.getName()));
            if (field.getType().equals(String.class)) {
                column.setType("varchar");
                column.setLength("255");
            } else if (field.getType().equals(Integer.class) ||
                    field.getType().equals(int.class)) {
                column.setType("int");
                column.setLength("11");
            } else if (field.getType().equals(Long.class) ||
                    field.getType().equals(long.class)) {
                column.setType("bigint");
            } else if (field.getType().equals(BigDecimal.class)) {
                column.setType("DECIMAL");
                column.setLength("10,2");
            } else if (field.getType().equals(Date.class) ||
                    field.getType().equals(java.sql.Date.class)) {
                column.setType("datetime");
            }
            column.setIsNotNull(Boolean.FALSE);
            column.setIsPrimaryKey(Boolean.FALSE);
            Comment comment = field.getAnnotation(Comment.class);
            if (comment != null && StringUtils.isNotEmpty(comment.value()))
                column.setComment(comment.value());
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            if (apiModelProperty != null && StringUtils.isNotEmpty(apiModelProperty.value()))
                column.setComment(apiModelProperty.value());
            columns.add(column);
        }
        // 判断父类
        Boolean hasBaseEntity = aClass.getSuperclass().equals(BaseEntity.class);
        if (hasBaseEntity)
            handleMysqlSuperColumn(columns);
        mysqlTableEntity.setColumns(columns);
        tableEntities.add(mysqlTableEntity);
        return true;
    }

    private static void handleMysqlSuperColumn(List<TableColumnEntity> columns) {
        columns.add(TableColumnEntity.builder()
                .name("ct_date")
                .type("datetime")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("创建时间")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("ct_user_id")
                .type("bigint")
                .length("20")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("创建人")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("lt_date")
                .type("datetime")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("更新时间")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("lt_user_id")
                .type("bigint")
                .length("20")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("更新人")
                .build());
    }

    private static void handlePgSuperColumn(List<TableColumnEntity> columns) {
        columns.add(TableColumnEntity.builder()
                .name("ct_date")
                .type("timestamp")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("创建时间")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("ct_user_id")
                .type("int8")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("创建人")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("lt_date")
                .type("timestamp")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("更新时间")
                .build());
        columns.add(TableColumnEntity.builder()
                .name("lt_user_id")
                .type("int8")
                .isNotNull(false)
                .isPrimaryKey(false)
                .comment("更新人")
                .build());
    }

    private static Boolean checkTableAnnotation(List<Field> fields, List<Class> generatorClasses) {
        long count = fields.stream().filter(e -> e.getAnnotation(TableId.class) != null).count();
        if (count > 1)
            return Boolean.FALSE;
        Boolean hasId = fields.stream().anyMatch(e -> e.getAnnotation(TableId.class) != null
                && generatorClasses.contains(e.getType()));
        return hasId;
    }

    /**
     * 将实体类反射成postgres类型
     *
     * @param tableEntities
     * @param aClass
     * @param generatorClasses
     * @return
     */
    private static Boolean handlePgTableEntities(List<TableEntity> tableEntities, Class<?> aClass, List<Class> generatorClasses, String schema, String dbName) {
        PostgresTableEntity postgresTableEntity = new PostgresTableEntity();
        TableName table = aClass.getAnnotation(TableName.class);
        if (table == null || StringUtils.isEmpty(table.value()))
            return false;

        List<Field> fields = Arrays.asList(aClass.getDeclaredFields());
        Boolean aBoolean = checkTableAnnotation(fields, generatorClasses);
        if (!aBoolean)
            return false;

        postgresTableEntity.setTableName(table.value());
        postgresTableEntity.setSchema(schema);
        postgresTableEntity.setDbName(dbName);
        List<TableColumnEntity> columns = new ArrayList<>();
        for (Field field : fields) {
            if (!generatorClasses.contains(field.getType()))
                continue;
            TableField tableFieldAno = field.getAnnotation(TableField.class);
            if (tableFieldAno != null && tableFieldAno.exist() == false)
                continue;
            if (field.getName().equals("serialVersionUID"))
                continue;
            TableColumnEntity column = new TableColumnEntity();
            TableId tableIdAno = field.getAnnotation(TableId.class);
            if (tableIdAno != null) {
                // id
                column.setName(StringUtils.isNotEmpty(tableIdAno.value()) ? tableIdAno.value() : "id");
                column.setComment(DdlConstant.idComment);
                column.setIsPrimaryKey(Boolean.TRUE);
                column.setIsNotNull(Boolean.TRUE);
                if (tableIdAno.type() == null) {
                    continue;
                } else {
                    switch (tableIdAno.type()) {
                        case NONE:
                            break;
                        case AUTO:
                            break;
                        case INPUT:
                            if (field.getType().equals(String.class)) {
                                column.setType("varchar");
                                column.setLength("32");
                            } else if (field.getType().equals(int.class) ||
                                    field.getType().equals(Integer.class)) {
                                column.setType("int4");
                            }
                            break;
                        case ASSIGN_ID:
                            column.setType("int8");
                            break;
                        case ASSIGN_UUID:
                            column.setType("varchar");
                            column.setLength("32");
                            break;
                        default: {
                        }
                    }
                }
                columns.add(column);
                continue;
            }
            column.setName(CamelUtil.camelToUnderline(field.getName()));
            if (field.getType().equals(String.class)) {
                column.setType("varchar");
                column.setLength("255");
            } else if (field.getType().equals(Integer.class)) {
                column.setType("int4");
            } else if (field.getType().equals(Long.class) ||
                    field.getType().equals(long.class)) {
                column.setType("int8");
            } else if (field.getType().equals(Date.class) ||
                    field.getType().equals(java.sql.Date.class)) {
                column.setType("timestamp");
            } else if (field.getType().equals(BigDecimal.class)) {
                column.setType("decimal");
                column.setLength("10,2");
            }
            column.setIsNotNull(Boolean.FALSE);
            column.setIsPrimaryKey(Boolean.FALSE);
            Comment comment = field.getAnnotation(Comment.class);
            if (comment != null && StringUtils.isNotEmpty(comment.value()))
                column.setComment(comment.value());
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            if (apiModelProperty != null && StringUtils.isNotEmpty(apiModelProperty.value()))
                column.setComment(apiModelProperty.value());
            columns.add(column);
        }
        // 判断父类
        Boolean hasBaseEntity = aClass.getSuperclass().equals(BaseEntity.class);
        if (hasBaseEntity)
            handlePgSuperColumn(columns);
        postgresTableEntity.setColumns(columns);
        tableEntities.add(postgresTableEntity);
        return true;
    }
}
